from datetime import datetime
import logging
import logging as log
import os
import re
import time
import random

from casdoor import User, User as CasUser
from casdoor.session import Session
from flask import flash, g, jsonify, redirect, request, session
from flask_appbuilder import Model
from flask_appbuilder.const import LOGMSG_WAR_SEC_LOGIN_FAILED
from flask_appbuilder.security.forms import LoginForm_db
from flask_appbuilder.security.views import AuthDBView, expose
from flask_login import login_user, logout_user
from sqlalchemy import Column, ForeignKey, Integer, Sequence, String
from werkzeug.security import check_password_hash, generate_password_hash

from myapp.const.response import BizError, wrap_response
from myapp.const.user import EnumUserStatus
from myapp.models.model_user import MyUser
from myapp.third.annotation.api import get_annotation_token
import myapp.third.auth as casdoor_auth
from myapp.third.auth.permission import batch_get_roles_for_user
from myapp.third.auth.sdk import (
    get_auth_code,
    get_sdk,
    logout,
    parse_token,
    parse_token_from_header,
)
from myapp.third.auth.user import update_password
from myapp.third.ccdk.api import get_ccdk_user_info
from myapp.third.knowledge.api import get_knowledge_token
from myapp.third.labelx.api import logout_labelx
import myapp.third.maas.api as maas_api
from myapp.third.pay.api import register_user_balance_account
from myapp.third.tencentcloud.sms import check_sms_code, gen_sms_code, send_sms
from myapp.third.zhentong.api import get_wzy_user_info
from myapp.utils import strings
from myapp.utils.exception import log_exception
from myapp.utils.pool import pool
from myapp.utils.strings import id_generator, is_valid_phone_number


class ABUserRole(Model):
    __tablename__ = 'ab_user_role'

    id = Column(Integer, Sequence('ab_user_role_id_seq'), primary_key=True)
    user_id = Column(Integer, ForeignKey('ab_user.id'))
    role_id = Column(Integer, ForeignKey('ab_role.id'))


class ABRole(Model):
    __tablename__ = 'ab_role'

    id = Column(Integer, primary_key=True)
    name = Column(String(64), nullable=False)


# 推送给管理员消息的函数
def push_admin(message):
    pass


# 推送消息给用户的函数
def push_message(receivers, message, link=None):
    pass


def json_response(message, status, result=None):
    if result is None:
        result = {}
    return jsonify({'message': message, 'status': status, 'result': result})


class MyCustomRemoteUserView:
    pass


def name_check(name):
    count = 0

    rule = '[_A-Za-z0-9]{6,20}$'

    curRes = re.fullmatch(rule, name, flags=0)
    if curRes is None:
        print('count:', count)
        return False

    for s in name:
        if s.isdigit():
            count = count + 1
            break

    my_re = re.compile(r'[A-Za-z]', re.S)
    res = re.findall(my_re, name)
    if len(res):
        count = count + 1

    if name.find('_') != -1:
        count = count + 1

    print('count:', count)
    if count < 2:
        return False
    return True


def get_role(arr):
    if arr is None or len(arr) == 0:
        return 'Common'
    all_role = {
        'Common': 1,
        'labelOp': 2,
        'labelProvider': 3,
        'labelWorker': 4,
        'labelReviewer': 5,
        'labelQa': 6,
        'labelBoss': 7,
        'Senior': 8,
        'Vip': 9,
        'Admin': 10,
    }
    role = max(arr, key=lambda x: all_role.get(x, 0))
    if not all_role.get(role):
        role = 'Common'
    return role


def get_zt_user_id(user_id: int):
    from myapp.app import db

    u = db.session.query(MyUser).filter_by(id=user_id).first()
    if u is None:
        return None
    else:
        if u.taichu is None or u.taichu == '':
            logging.warning(f'[get_zt_user_id] user_id: {user_id} get none casdoor_id: {u.taichu}')
        else:
            return u.taichu
    # sdk = get_sdk()
    # casdoor_user = sdk.get_user(u.username)
    # if u is None:
    #     return None
    # return casdoor_user.google


class Myauthdbview(AuthDBView):
    login_template = 'appbuilder/general/security/login_db.html'
    unregister_suffix = '*unregister'
    max_batch_size = 50
    weixin_default_password = 'wair1234!'
    internal_login_valid_source_list = ['weixin', 'sc_net']

    @expose('/login/', methods=['GET', 'POST'])
    # @pysnooper.snoop(watch_explode=('form',))
    def login(self):
        if 'rtx' in request.args:
            if request.args.get('rtx'):
                username = request.args.get('rtx')
                user = self.appbuilder.sm.find_user(username)
                if user:
                    login_user(user, remember=True)
                    return redirect(self.appbuilder.get_url_for_index)

        if g.user is not None and g.user.is_authenticated:
            return redirect(self.appbuilder.get_url_for_index)

        form = LoginForm_db()
        # 如果提交请求。就是认证
        if form.validate_on_submit():
            username = form.username.data
            password = form.password.data

            user = self.appbuilder.sm.find_user(username=username)
            if user is None:
                user = self.appbuilder.sm.find_user(email=username)
            if user is None or (not user.is_active):
                log.info(LOGMSG_WAR_SEC_LOGIN_FAILED.format(username))
                user = None
            elif check_password_hash(user.password, password):
                self.appbuilder.sm.update_user_auth_stat(user, True)
            elif user.password == password:
                self.appbuilder.sm.update_user_auth_stat(user, True)
            else:
                self.appbuilder.sm.update_user_auth_stat(user, False)
                log.info(LOGMSG_WAR_SEC_LOGIN_FAILED.format(username))
                user = None

            if not user:
                user = self.appbuilder.sm.find_user(form.username.data)
                if user:
                    # 有用户，但是密码不对
                    flash('发现用户%s已存在，但输入密码不对' % form.username.data, 'warning')
                    return redirect(self.appbuilder.get_url_for_login)
                else:
                    # 没有用户的时候自动注册用户
                    user = self.appbuilder.sm.auth_user_remote_org_user(
                        username=form.username.data, org_name='', password=form.password.data
                    )
                    flash('发现用户%s不存在，已自动注册' % form.username.data, 'warning')
            login_user(user, remember=True)
            return redirect(self.appbuilder.get_url_for_index)
        return self.render_template(
            self.login_template, title=self.title, form=form, appbuilder=self.appbuilder
        )

    @expose('v1/login/', methods=['GET', 'POST'])
    def login_v1(self):
        username = request.json.get('username')
        password = request.json.get('password', '')
        sms_code = request.json.get('sms_code')
        scene = request.json.get('scene', 'login')
        source = request.json.get('source')

        if not username:
            return json_response('用户名或者手机号不能为空', 1)
        if not password and not sms_code:
            if not password:
                return json_response('密码不能为空', 1)
            elif not sms_code:
                return json_response('验证码不能为空', 1)

        user = self.appbuilder.sm.find_user(username=username)
        if not user:
            from myapp.app import db

            user = db.session.query(MyUser).filter_by(phone=str(username)).first()

        if not user:
            return json_response('用户不存在', 1)

        if not user.is_active:
            log.info(LOGMSG_WAR_SEC_LOGIN_FAILED.format(username))
            return json_response('用户尚未激活，请联系管理员', 1)

        if user.status != EnumUserStatus.normal.value:
            if user.status == EnumUserStatus.disabled.value:
                return json_response('用户被禁用，请联系管理员', 1)
            if user.status == EnumUserStatus.expired.value:
                return json_response('用户已过期，请联系管理员', 1)
            if user.status == EnumUserStatus.unregister.value:
                return json_response('用户已注销，请联系管理员', 1)

        # 如果用户过期了，但是状态还没有改，将用户状态改成过期(惰性更新)
        if user.expired_time:
            if int(time.time()) > user.expired_time:
                user.status = EnumUserStatus.expired.value
                self.appbuilder.sm.update_user_info(user=user)
                return json_response('用户已过期，请联系管理员', 1)

        if password:
            if check_password_hash(user.password, password) or user.password == password:
                self.appbuilder.sm.update_user_auth_stat(user, True)
            else:
                self.appbuilder.sm.update_user_auth_stat(user, False)
                return json_response('账号或密码错误', 1)
        elif sms_code:
            check_msg = check_sms_code(str(username), sms_code, scene)
            if check_msg:
                return json_response(check_msg, 1)

        login_user(user, remember=True)
        # 查询用户角色信息，并返回; 无角色信息的默认为体验用户
        roles = self.appbuilder.sm.get_user_role(user.username)

        annotation_token, knowledge_token = '', ''
        if source == 'ai_platform':  # 只有ai平台需要标注登录token
            annotation_token = get_annotation_token(username)
            if not annotation_token:
                return json_response('登录失败，请联系管理员', 2)
            knowledge_token = get_knowledge_token(user.id)
            if not knowledge_token:
                return json_response('登录失败，请联系管理员', 3)
        result = {
            'id': user.id,
            'username': user.username,
            'nickname': user.nickname,
            'email': user.email,
            'phone': user.phone,
            'role': get_role(roles),
            'annotation_token': annotation_token,
            'knowledge_token': knowledge_token,
        }
        return json_response('success', 0, result)

    @expose('/send_sms_code/', methods=['POST'])
    def send_sms_code(self):
        phone = request.json.get('phone')
        scene = request.json.get('scene', 'default')
        logging.info('scene: %s', scene)

        if phone is None:
            return json_response('手机号不可为空', 1)
        from myapp.app import db

        user = db.session.query(MyUser).filter_by(phone=str(phone)).first()

        if scene == 'login':
            if user is None:
                return json_response('手机号未被注册', 1)
        elif scene == 'register':
            if user is not None:
                return json_response('手机号已被占用', 1)
        elif scene == 'unregister':
            if user is None:
                return json_response('手机号未被注册', 1)

        code = gen_sms_code(phone, scene)

        err_msg, status_code = send_sms(phone, code, scene)
        if err_msg is not None:
            return json_response(err_msg, 1, {'error_code': status_code})
        return json_response('success', 0)

    @expose('/reset_password', methods=['POST'])
    def reset_password(self):
        sms_code = request.json.get('sms_code')
        phone = request.json.get('phone')
        scene = request.json.get('scene', 'reset_pwd')

        new_password = request.json.get('password')
        repassword = request.json.get('repassword')
        if new_password != repassword:
            return json_response('两次输入的密码不一致', 1)
        check_msg = check_sms_code(phone, sms_code, scene)
        if check_msg is not None:
            return json_response(check_msg, 1)
        pwd = generate_password_hash(new_password)

        from myapp.app import db

        u = db.session.query(MyUser).filter_by(phone=str(phone)).first()
        if u is None:
            return json_response('用户不存在', 1)
        u.password = pwd
        update_password(u.taichu, new_password)

        db.session.commit()
        return json_response('success', 0)

    @expose('/user/set-password', methods=['POST'])
    @wrap_response
    def set_password(self):
        req = request.json
        oldPassword = req.get('oldPassword')
        newPassword = req.get('newPassword')

        if oldPassword is None or newPassword is None:
            raise BizError(message='密码不能为空')

        authorization_value = request.headers.get('Authorization')
        token = parse_token_from_header(authorization_value)

        casdoor_auth.user.set_password(oldPassword, newPassword, token)

    @expose('/register/', methods=['POST'])
    def register(self):
        form = LoginForm_db()
        req = request.json

        phone = req.get('phone', None)
        sms_code = req.get('sms_code')
        scene = req.get('scene', 'register')
        if phone is not None:
            if sms_code is None:
                return json_response('短信验证码是必填项', 1)
            check_msg = check_sms_code(phone, sms_code, scene)
            if check_msg is not None:
                return json_response(check_msg, 1)

        if form.validate_on_submit():
            user = self.appbuilder.sm.find_user(username=form.username.data)
            from myapp.app import db

            if phone is not None:
                u_by_phone = db.session.query(MyUser).filter_by(phone=str(phone)).first()
                if u_by_phone:
                    return json_response('该手机号已经被占用', 1)
            if user:
                # 有用户
                flash('发现用户%s已存在' % form.username.data, 'warning')
                return json_response('用户已存在', 1)
            else:
                # 没有用户的时候注册用户
                print('name:', form.username.data)
                if not name_check(form.username.data):
                    return json_response('用户名不满足规则 ', 1)

                if req.get('email') is not None:
                    # 校验邮箱唯一
                    email = req.get('email')
                    user = self.appbuilder.sm.find_user(email=email)
                    if user is not None:
                        return json_response('邮箱已被注册 ', 1)
                else:
                    email = ''

                # 判断用户有没有注销
                u_unregister = (
                    db.session.query(MyUser)
                    .filter_by(username=(str(form.username.data) + self.unregister_suffix))
                    .first()
                )
                if u_unregister:
                    return json_response('用户名已被占用，请更换用户名注册 ', 1)

                source = req.get('source')

                pwd = generate_password_hash(form.password.data)
                nickname = '' if req.get('nickname') is None else req.get('nickname')
                user = self.appbuilder.sm.auth_user_remote_org_user(
                    username=form.username.data,
                    org_name=source,
                    password=pwd,
                    email=email,
                    first_name='',
                    last_name='',
                    nickname=nickname,
                    phone=phone,
                )
                return json_response('success', 0)
        return json_response('注册失败 ', 1)

    @expose('/logout')
    def logout(self):
        login_url = request.host_url.strip('/') + '/login/'
        session.pop('user', None)
        g.user = None
        logout_user()
        return redirect(login_url)

    @expose('/v1/logout', methods=['GET', 'POST'])
    @wrap_response
    def logout_v1(self):
        with log_exception:
            logout_labelx(uid=g.user.id)
        session.pop('user', None)
        g.user = None
        logout_user()

        # 密码模式授权，不涉及到session
        authorization_value = request.headers.get('Authorization')
        token = parse_token_from_header(authorization_value)
        if token:
            user = parse_token(token)
            logging.info(f'user:{user}')
            username = user.get('name')
            sdk = get_sdk()
            if username and len(username) > 0:
                sess = sdk.get_session(username, sdk.application_name)
                if sess is None:
                    return
                logging.info(f'sess:{sess}')
                for sid in sess.sessionId:
                    logout(sid, token)

                sdk.delete_session(
                    Session.from_dict(
                        {
                            'name': username,
                            'owner': user.get('owner', ''),
                            'application': sdk.application_name,
                        }
                    )
                )

    @expose('/user/list', methods=['POST'])
    @wrap_response
    def user_list(self):
        req = request.json

        page = req.get('page', 0)
        page_size = req.get('page_size', 10)
        filters = req.get('filters', [])

        if page < 0:
            page = 0
        if page_size < 0:
            page_size = 0
        if page_size > 100:
            page_size = 100

        sess = self.appbuilder.sm.get_session

        query = sess.query(MyUser)
        # if not g.user.is_admin():
        #     query = query.filter(MyUser.created_by_fk == g.user.id)

        for f in filters:
            if f['col'] == 'status':
                value_type = type(f['value'])
                if value_type == list:
                    vals = []
                    for v in f['value']:
                        vals.append(EnumUserStatus.get_val(v))
                    f['value'] = vals
                else:
                    f['value'] = EnumUserStatus.get_val(f['value'])

        for f in filters:
            if f['col'] == 'role' and f['opr'] == 'in':
                query = (
                    query.join(ABUserRole, ABUserRole.user_id == MyUser.id)
                    .join(ABRole, ABUserRole.role_id == ABRole.id)
                    .filter(ABRole.name.in_(f['value']))
                )
                continue

            col = getattr(MyUser, f['col'])
            if f['opr'] == 'eq':
                query = query.filter(col == f['value'])
            elif f['opr'] == 'gte':
                query = query.filter(col >= f['value'])
            elif f['opr'] == 'lt':
                query = query.filter(col < f['value'])
            elif f['opr'] == 'in':
                query = query.filter(col.in_(f['value']))
            elif f['opr'] == 'ct':
                query = query.filter(col.like(f'%{f["value"]}%'))
            else:
                raise BizError(1000, f"不支持的filter,opr:{f['opr']}")

        count = query.count()
        results = (
            query.order_by(MyUser.created_on.desc())
            .offset(page * page_size)
            .limit(page_size)
            .all()
        )

        ret = []

        # 批量获取用户角色
        user_ids = [item.taichu for item in results]
        user_roles_map = {}
        with log_exception:
            user_roles_map = batch_get_roles_for_user(user_ids)

        for item in results:
            roles = self.appbuilder.sm.get_user_role(item.username)
            # roles = user_roles_map.get(item.taichu, [])
            ret.append(
                {
                    'id': item.id,
                    'username': item.username,
                    'nickname': item.nickname,
                    'phone': item.phone,
                    'email': item.email,
                    'info': item.info,
                    'status': EnumUserStatus.get_name(item.status),
                    'role': get_role(roles),
                    'expired_time': item.expired_time,
                    'created_on': item.created_on.strftime('%Y-%m-%d %H:%M:%S'),
                    'changed_on': item.changed_on.strftime('%Y-%m-%d %H:%M:%S'),
                }
            )

        return {'count': count, 'data': ret}

    def get_roles_by_ab_user_role(self, user_ids):
        if not user_ids:
            return {}
        # 查询 ABUserRole 表，获取用户角色
        from myapp.app import db

        user_roles = (
            db.session.query(ABUserRole.user_id, ABUserRole.role_id)
            .filter(ABUserRole.user_id.in_(user_ids))
            .all()
        )
        # 将角色信息映射到用户ID
        roles_dict = {}
        for user_id, role_id in user_roles:
            if user_id not in roles_dict:
                roles_dict[user_id] = []
            roles_dict[user_id].append(role_id)
        logging.info(f'roles_dict:{roles_dict}')
        return roles_dict

    @expose('user/info/', methods=['GET', 'POST'])
    def get_user_info(self):
        user = self.appbuilder.sm.get_user_by_id(g.user.id)
        roles = user.get_roles()
        if request.method == 'GET':
            maas_user_info = {}
            with log_exception:
                authorization = request.headers.get('Authorization')
                token = parse_token_from_header(authorization)
                maas_user_info = maas_api.get_user_info(token)

            result = {
                'id': user.id,
                'username': user.username,
                'nickname': user.nickname,
                'email': user.email,
                'phone': user.phone,
                'roles': roles,
                'role': get_role(roles),
                'info': user.info,
                'tenant_id': ''
                if user.tenant_id == '' or user.tenant_id == user.username
                else user.tenant_id,
            }
            maas_user_info.update(result)

            audit_type = maas_user_info.get('audit_type', '')
            if audit_type is None or audit_type == '':
                audit_type = '未认证'
            maas_user_info['audit_type'] = audit_type

            return json_response('success', 0, maas_user_info)
        if request.method == 'POST':
            req = request.json
            if req.get('id') is None or req.get('username') is None:
                return json_response('用户id或者名称必填', 1)

            # 修改自己的信息
            if g.user.id == req.get('id'):
                if user.username != req.get('username'):
                    return json_response('用户名称不可更改', 1)
                if req.get('nickname') is not None:
                    user.nickname = req.get('nickname')
                if req.get('info') is not None:
                    user.info = req.get('info')

                # 邮箱修改需要输入密码
                if req.get('email') is not None and req.get('email') != user.email:
                    print('uuu:', check_password_hash(user.password, req.get('old_pwd')))
                    if req.get('old_pwd') is None or not (
                        check_password_hash(user.password, req.get('old_pwd'))
                        or user.password == req.get('old_pwd')
                    ):
                        return json_response('原密码不正确', 1)
                    else:
                        new_user = self.appbuilder.sm.find_user(email=req.get('email'))
                        if new_user is not None:
                            return json_response('邮箱已被注册', 1)
                        user.email = req.get('email')

                # 修改密码
                password_changed = False
                if req.get('new_pwd') is not None:
                    if not (
                        check_password_hash(user.password, req.get('old_pwd'))
                        or user.password == req.get('old_pwd')
                    ):
                        return json_response('密码错误', 2)
                    if req.get('new_pwd') != req.get('confirm_pwd'):
                        return json_response('密码不一致', 1)
                    user.password = generate_password_hash(req.get('new_pwd'))
                    password_changed = True

                # 更新
                if self.appbuilder.sm.update_user_info(user) is False:
                    return json_response('更新失败', 1)

                if password_changed:
                    update_password(user.taichu, req.get('new_pwd'))

                result = {
                    'id': user.id,
                    'username': user.username,
                    'nickname': user.nickname,
                    'email': user.email,
                    'phone': user.phone,
                    'roles': roles,
                    'role': get_role(roles),
                    'info': user.info,
                }
                return json_response('success', 0, result)

            # todo:管理员角色可以直接他人修改,逻辑待补充
            elif 'Admin' in roles:
                pass

            return json_response('请登录', 1)

    @expose('user/auth/', methods=['POST'])
    def get_user_auth(self):
        req = request.json
        if req.get('password') is None:
            return json_response('请输入密码', 1)
        user = self.appbuilder.sm.get_user_by_id(g.user.id)
        if check_password_hash(user.password, req.get('password')) or user.password == req.get(
            'password'
        ):
            result = {'auth': user.secret}
            return json_response('success', 0, result)
        return json_response('密码错误', 1)

    @expose('user/unregister/', methods=['POST'])
    def unregister(self):
        req = request.json
        phone = req.get('phone', None)
        sms_code = req.get('sms_code')
        scene = req.get('scene', 'unregister')
        # password = req.get("password")
        # if password is None:
        #     return json_response('请输入密码', 1)
        if phone is None:
            return json_response('请输入手机号', 1)
        if sms_code is None:
            return json_response('请输入验证码', 1)

        user = self.appbuilder.sm.get_user_by_id(g.user.id)
        if user is None:
            return json_response('用户不存在', 1)

        if user.status == EnumUserStatus.unregister.value:
            return json_response('用户已注销', 1)

        if user.phone is None:
            return json_response('用户原手机号为空，不能注销', 1)

        if user.phone != phone:
            return json_response('手机号与注册手机号不匹配，不能注销', 1)

        check_msg = check_sms_code(phone, sms_code, scene)
        if check_msg is not None:
            return json_response(check_msg, 1)

        # 注销账户采用更改用户信息的方式，用户名增加特定后缀(最新版去掉该逻辑，直接改状态)，
        # 邮箱和手机号添加随机字符串
        user.status = EnumUserStatus.unregister.value
        user.email = user.email + id_generator(size=6)
        user.phone = user.phone + id_generator(size=6)
        casdoor_id = user.taichu
        if casdoor_id and len(casdoor_id) > 0:
            sdk = get_sdk()
            sdk.delete_user(CasUser.from_dict({'id': str(casdoor_id), 'owner': sdk.org_name}))
            user.taichu = casdoor_id + '-del'
        # 更新
        if self.appbuilder.sm.update_user_info(user) is False:
            return json_response('更新失败', 1)
        result = {}

        return json_response('success', 0, result)

    @expose('internal/user/get_user_info/', methods=['POST'])
    def internal_get_user_info(self):
        req = request.json
        user_id = req.get('user_id')
        username = req.get('username')
        phone = req.get('phone')
        # 如果没有传任何参数，返回空数据
        if not user_id and not username and not phone:
            return json_response('success', 0, {})

        user = None
        from myapp.app import db

        q = db.session.query(MyUser)
        if user_id:
            q = q.filter_by(taichu=user_id)
        if username:
            q = q.filter_by(username=username)
        if phone:
            q = q.filter_by(phone=phone)

        user = q.first()

        # if user_id:
        #     user = db.session.query(MyUser).filter_by(id=user_id).first()
        # elif username:
        #     user = db.session.query(MyUser).filter_by(username=username).first()
        # elif phone:
        #     user = db.session.query(MyUser).filter_by(phone=phone).first()

        if user:
            uid = 0
            try:
                uid = int(user.taichu)
            except Exception:
                pass

            return json_response(
                'success',
                0,
                {
                    'id': uid,
                    'username': user.username,
                    'nickname': user.nickname,
                    'email': user.email,
                    'phone': user.phone,
                },
            )
        return json_response('success', 0, {})

    @expose('internal/user/batch_get_user_info/', methods=['POST'])
    def batch_get_user_info(self):
        req = request.json
        user_ids = req.get('user_ids')
        if len(user_ids) > self.max_batch_size:
            return json_response('超过限定批次50', 1, {})

        result = []
        from myapp.app import db

        if user_ids:
            users = db.session.query(MyUser).filter(MyUser.taichu.in_(user_ids)).all()
            for user in users:
                if user is not None:
                    uid = 0
                    try:
                        uid = int(user.taichu)
                    except Exception:
                        pass
                    result.append(
                        {
                            'id': uid,
                            'username': user.username,
                            'nickname': user.nickname,
                            'email': user.email,
                            'phone': user.phone,
                        }
                    )
            return json_response('success', 0, result)

        return json_response('success', 0, {})

    @expose(
        'internal/user/register', methods=['POST']
    )  # 内部注册接口，微信小程序注册等，只需要手机号
    def internal_user_register(self):
        req = request.json
        phone = req.get('phone')
        source = req.get('source')
        if not phone:
            return json_response('手机号必须填', 1)

        if not source:
            return json_response('source必须填', 1)

        # 校验source来源
        if source not in self.internal_login_valid_source_list:
            return json_response('非法source', 1)

        from myapp.app import db

        u_by_phone = db.session.query(MyUser).filter_by(phone=str(phone)).first()
        if u_by_phone:
            # 有用户
            return json_response('用户已注册', 0)
        else:
            # 没有用户的时候注册用户
            email = ''
            pwd = generate_password_hash(self.weixin_default_password)
            username = source + '_' + id_generator(6)
            nickname = '' if req.get('nickname') is None else req.get('nickname')
            self.appbuilder.sm.auth_user_remote_org_user(
                username=username,
                org_name=source,
                password=pwd,
                email=email,
                first_name='',
                last_name='',
                nickname=nickname,
                phone=phone,
            )
            return json_response('success', 0)

    @expose(
        'internal/user/login', methods=['POST']
    )  # 内部登录接口，微信小程序登录等，只需要手机号
    def internal_user_login(self):
        req = request.json
        phone = req.get('phone')
        source = req.get('source')

        if not phone:
            return json_response('手机号必须填', 1)
        if not source:
            return json_response('source必须填', 1)

        # 内部登录接口校验source合法性
        if source not in self.internal_login_valid_source_list:
            return json_response('非法source', 1)

        from myapp.app import db

        user = db.session.query(MyUser).filter_by(phone=str(phone)).first()
        result = {}
        if user:
            # 有用户
            login_user(user, remember=True)
            # 查询用户角色信息，并返回; 无角色信息的默认为体验用户

            result = {
                'id': user.id,
                'username': user.username,
                'nickname': user.nickname,
                'email': user.email,
                'phone': user.phone,
            }
            return json_response('success', 0, result)

        return json_response('登录失败', 1)

    @expose('/auth/callback_ccdk', methods=['GET', 'POST'])
    @wrap_response
    def ccdk_callback(self):
        token = request.args.get('token')
        if token is None:
            raise BizError('token required')
        user_info = get_ccdk_user_info(token)
        if user_info is None:
            raise BizError(os.getenv('CCDK_AUTH_HOST', ''))

        name = f"ccdk_{user_info['username']}"
        ccdk_phone = user_info['phone']
        real_name = user_info['realName']

        sdk = get_sdk()
        logging.info(f'get_user:{name}')
        casdoor_user = sdk.get_user(name)
        if casdoor_user is None:
            #create casdoor user
            alphabet = 'abcdefghijklmnopqrstuvwxyz1234567890'
            pwd = name + ''.join(random.sample(alphabet, 3))
            new_user = User.new(
                owner='',
                name=name,
                created_time=datetime.now().strftime('%Y-%m-%dT%H:%M:%SZ'),
                display_name=user_info['nickname'],
            )
            new_user.phone = user_info['phone']
            new_user.password = pwd
            new_user.google = pwd
            new_user.signupApplication = 'ai-platform'
            new_user.type = 'normal-user'
            sdk.add_user(new_user)

        user = sdk.get_user(name)
        logging.info(f'get_oauth_token:{token}')
        token = sdk.get_oauth_token(username=user.name, password=user.google)
        if 'error' in token:
            raise BizError(f"[{token['error']}] {token.get('error_description', '')}")
        access_token = token.get('access_token')
        user_info = parse_token(access_token)

        local_user = (
            self.appbuilder.sm.get_session.query(MyUser).filter_by(taichu=str(user.id)).first()
        )

        if local_user is None:
            # 没有,则注册一个
            # 检查username是否被占用
            logging.info('user not found, try to register new one')
            origin_username = user_info['name']
            username = user_info['name']
            u = (
                self.appbuilder.sm.get_session.query(MyUser.id)
                .filter_by(username=str(username))
                .first()
            )
            if u is not None:
                username = f'u{strings.id_generator(10)}'

            email = user_info.get('email', origin_username)
            # 检查email是否被占用
            u = self.appbuilder.sm.get_session.query(MyUser.id).filter_by(email=str(email)).first()
            if u is not None:
                email = f'{strings.id_generator(10)}'

            first_name = user_info.get('firstName', origin_username)
            last_name = user_info.get('lastName', origin_username)
            nickname = user_info.get('displayName', origin_username)
            phone = user_info.get('phone', '')
            if not is_valid_phone_number(phone):
                phone = ''

            local_user = self.appbuilder.sm.auth_user_remote_org_user(
                username=username,
                org_name='',
                taichu=user_info['id'],
                password='',
                email=email,
                first_name=first_name,
                phone=phone,
                last_name=last_name,
                nickname=nickname,
            )

        if not local_user.is_active:
            log.info(LOGMSG_WAR_SEC_LOGIN_FAILED.format(local_user.username))
            raise BizError(1, '用户尚未激活，请联系管理员')

        if local_user.status != EnumUserStatus.normal.value:
            if local_user.status == EnumUserStatus.disabled.value:
                raise BizError(1, '用户被禁用，请联系管理员')
            if local_user.status == EnumUserStatus.expired.value:
                raise BizError(1, '用户已过期，请联系管理员')
            if local_user.status == EnumUserStatus.unregister.value:
                raise BizError(1, '用户已注销，请联系管理员')

            # 如果用户过期了，但是状态还没有改，将用户状态改成过期(惰性更新)
        if local_user.expired_time:
            if int(time.time()) > local_user.expired_time:
                local_user.status = EnumUserStatus.expired.value
                self.appbuilder.sm.update_user_info(user=local_user)
                raise BizError(1, '用户已过期，请联系管理员')

        session['user_id'] = local_user.id
        login_user(local_user, remember=True)

        roles = local_user.get_roles()
        with log_exception:
            t1 = pool.submit(
                maas_api.register_user,
                int(user_info['id']),
                local_user.nickname,
                user_info['name'],
                user_info.get('phone', ''),
                get_role(roles),
            )
            t2 = pool.submit(register_user_balance_account, int(local_user.taichu))

            t1.result()
            t2.result()

        return {
            'id': local_user.id,
            'username': name,
            'nickname': real_name,
            'phone': ccdk_phone,
            'role': get_role(roles),
            'roles': local_user.get_roles(),
            'access_token': access_token,
        }



    @expose('/auth/callback_zt', methods=['GET', 'POST'])
    # @pysnooper.snoop(watch_explode=('user_info',))
    @wrap_response
    def oidc_zt_callback(self):
        code = request.args.get('code')
        if code is None:
            raise BizError('code required')
        """调用接口请求政通的接口获取用户信息"""
        user_info = get_wzy_user_info(code)
        # user_info = get_mock_user_info(code)
        if user_info is None:
            raise BizError(os.getenv('ZHENG_TONG_URL', ''))
        logging.info(f'user_info:{user_info}')
        zt_user_id = user_info['userId']
        zt_user_account = user_info['account']
        # zt_user_nick_name = user_info['nickName']
        zt_user_real_name = user_info['realName']
        zt_user_phone = user_info['phone']
        # zt_user_id_card = user_info['idCardNo']
        user_status = user_info['status']
        if user_status == 0:
            raise BizError('用户已禁用')
        sdk = get_sdk()
        casdoor_user = sdk.get_user(zt_user_account)
        if casdoor_user is None:
            new_user = User.new(
                owner='',
                name=zt_user_account,
                created_time=datetime.now().strftime('%Y-%m-%dT%H:%M:%SZ'),
                display_name=zt_user_real_name,
            )
            new_user.phone = zt_user_phone
            # zt_user_id 作为用户密码，google用于存储政通用户id
            new_user.password = zt_user_id
            new_user.google = zt_user_id
            new_user.signupApplication = 'ai-platform'
            new_user.type = 'normal-user'
            sdk.add_user(new_user)
        else:
            if casdoor_user.phone != zt_user_phone:
                casdoor_user.phone = zt_user_phone
                # 更新用户手机号
                # sdk.update_user(casdoor_user)
            if casdoor_user.displayName != zt_user_real_name:
                casdoor_user.displayName = zt_user_real_name
                # 更新用户昵称
                # sdk.update_user(casdoor_user)

        user = sdk.get_user(zt_user_account)
        token = sdk.get_oauth_token(username=user.name, password=user.google)
        logging.info(f'get_oauth_token:{token}')
        if 'error' in token:
            raise BizError(f"[{token['error']}] {token.get('error_description', '')}")
        access_token = token.get('access_token')
        user_info = parse_token(access_token)

        local_user = (
            self.appbuilder.sm.get_session.query(MyUser).filter_by(taichu=str(user.id)).first()
        )

        if local_user is None:
            # 没有,则注册一个
            # 检查username是否被占用
            logging.info('user not found, try to register new one')
            origin_username = user_info['name']
            username = user_info['name']
            u = (
                self.appbuilder.sm.get_session.query(MyUser.id)
                .filter_by(username=str(username))
                .first()
            )
            if u is not None:
                username = f'u{strings.id_generator(10)}'

            email = user_info.get('email', origin_username)
            # 检查email是否被占用
            u = self.appbuilder.sm.get_session.query(MyUser.id).filter_by(email=str(email)).first()
            if u is not None:
                email = f'{strings.id_generator(10)}'

            first_name = user_info.get('firstName', origin_username)
            last_name = user_info.get('lastName', origin_username)
            nickname = user_info.get('displayName', origin_username)
            phone = user_info.get('phone', '')
            if not is_valid_phone_number(phone):
                phone = ''

            local_user = self.appbuilder.sm.auth_user_remote_org_user(
                username=username,
                org_name='',
                taichu=user_info['id'],
                password='',
                email=email,
                first_name=first_name,
                phone=phone,
                last_name=last_name,
                nickname=nickname,
            )

        if not local_user.is_active:
            log.info(LOGMSG_WAR_SEC_LOGIN_FAILED.format(local_user.username))
            raise BizError(1, '用户尚未激活，请联系管理员')

        if local_user.status != EnumUserStatus.normal.value:
            if local_user.status == EnumUserStatus.disabled.value:
                raise BizError(1, '用户被禁用，请联系管理员')
            if local_user.status == EnumUserStatus.expired.value:
                raise BizError(1, '用户已过期，请联系管理员')
            if local_user.status == EnumUserStatus.unregister.value:
                raise BizError(1, '用户已注销，请联系管理员')

            # 如果用户过期了，但是状态还没有改，将用户状态改成过期(惰性更新)
        if local_user.expired_time:
            if int(time.time()) > local_user.expired_time:
                local_user.status = EnumUserStatus.expired.value
                self.appbuilder.sm.update_user_info(user=local_user)
                raise BizError(1, '用户已过期，请联系管理员')

        session['user_id'] = local_user.id
        login_user(local_user, remember=True)

        roles = local_user.get_roles()
        with log_exception:
            t1 = pool.submit(
                maas_api.register_user,
                int(user_info['id']),
                local_user.nickname,
                user_info['name'],
                user_info.get('phone', ''),
                get_role(roles),
            )
            t2 = pool.submit(register_user_balance_account, int(local_user.taichu))

            t1.result()
            t2.result()

        return {
            'id': local_user.id,
            'username': zt_user_account,
            'nickname': zt_user_real_name,
            'phone': zt_user_phone,
            'role': get_role(roles),
            'roles': local_user.get_roles(),
            'access_token': access_token,
        }

    @expose('/auth/get_auth_code', methods=['GET'])
    @wrap_response
    def get_auth_code(self):
        ret = get_auth_code(request.headers.get('Authorization'))
        if 'err' in ret:
            raise BizError(ret['err'])
        return ret

    @expose('/auth/callback', methods=['GET', 'POST'])
    @wrap_response
    def oidc_callback(self):
        code = request.args.get('code')
        if code is None:
            raise BizError('code required')
        sdk = get_sdk()
        token = sdk.get_oauth_token(code=code)
        if 'error' in token:
            raise BizError(f"[{token['error']}] {token.get('error_description', '')}")
        access_token = token.get('access_token')

        user_info = parse_token(access_token)
        local_user = (
            self.appbuilder.sm.get_session.query(MyUser)
            .filter_by(taichu=str(user_info['id']))
            .first()
        )

        if local_user is None:
            # 没有,则注册一个
            # 检查username是否被占用
            logging.info('user not found, try to register new one')
            origin_username = user_info['name']
            username = user_info['name']
            u = (
                self.appbuilder.sm.get_session.query(MyUser.id)
                .filter_by(username=str(username))
                .first()
            )
            if u is not None:
                username = f'u{strings.id_generator(10)}'

            email = user_info.get('email', origin_username)
            # 检查email是否被占用
            u = self.appbuilder.sm.get_session.query(MyUser.id).filter_by(email=str(email)).first()
            if u is not None:
                email = f'{strings.id_generator(10)}'

            first_name = user_info.get('firstName', origin_username)
            last_name = user_info.get('lastName', origin_username)
            nickname = user_info.get('displayName', origin_username)
            phone = user_info.get('phone', '')
            if not is_valid_phone_number(phone):
                phone = ''

            local_user = self.appbuilder.sm.auth_user_remote_org_user(
                username=username,
                org_name='',
                taichu=user_info['id'],
                password='',
                email=email,
                first_name=first_name,
                phone=phone,
                last_name=last_name,
                nickname=nickname,
            )

        if not local_user.is_active:
            log.info(LOGMSG_WAR_SEC_LOGIN_FAILED.format(local_user.username))
            raise BizError(406, '用户尚未激活，请联系管理员', access_token)

        if local_user.status != EnumUserStatus.normal.value:
            if local_user.status == EnumUserStatus.disabled.value:
                raise BizError(406, '用户被禁用，请联系管理员', access_token)
            if local_user.status == EnumUserStatus.expired.value:
                raise BizError(406, '用户已过期，请联系管理员', access_token)
            if local_user.status == EnumUserStatus.unregister.value:
                raise BizError(406, '用户已注销，请联系管理员', access_token)
        # 如果用户过期了，但是状态还没有改，将用户状态改成过期(惰性更新)
        if local_user.expired_time:
            if int(time.time()) > local_user.expired_time:
                local_user.status = EnumUserStatus.expired.value
                self.appbuilder.sm.update_user_info(user=local_user)
                raise BizError(406, '用户已过期，local_user',access_token)

        session['user_id'] = local_user.id
        login_user(local_user, remember=True)

        roles = local_user.get_roles()
        with log_exception:
            t1 = pool.submit(
                maas_api.register_user,
                int(user_info['id']),
                local_user.nickname,
                user_info['name'],
                user_info.get('phone', ''),
                get_role(roles),
            )
            t2 = pool.submit(register_user_balance_account, int(local_user.taichu))

            t1.result()
            t2.result()

        return {
            'id': local_user.id,
            'tenant_id': local_user.tenant_id,
            'username': local_user.username,
            'nickname': local_user.nickname,
            'email': local_user.email,
            'phone': local_user.phone,
            'role': get_role(roles),
            'roles': local_user.get_roles(),
            'access_token': access_token,
        }
